package ch.hslu.gl20vscpudemo;

import android.graphics.Bitmap;

/**
 * CPU Renderer
 * perform image processing with the cpu
 */
public class CpuRenderer {

	/**
	 * apply a kernel to the source image and get an image
	 * @param src image
	 * @param w image width
	 * @param h image height
	 * @param kernel Kernel
	 * @return result
	 */
	public static Bitmap applyKernel(int[] src, int w, int h, int[][] kernel) {
		Bitmap dst = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
		int kmw = (kernel[0].length - 1) / 2; // kernel mean width
		int kmh = (kernel.length - 1) / 2; // kernel mean height

		int r, g, b;
		
		// calculate new picture without border
			for (int y = kernel.length; y < (h - kernel.length); y++) {
				// set pixel at pos. y
				for (int x = kernel[0].length; x < (w - kernel[0].length); x++) {
					// set pixel at pos. x
					// read neighbor and calculate new pixel
					r = 0;g = 0;b = 0;
					for (int j = (y - kmh), ky = 0; j < (y + kmh + 1); j++, ky++) {
						for (int i = (x - kmw), kx = 0; i < (x + kmw + 1); i++, kx++) {
							int k = kernel[kx][ky]; // weight kernel
							r += (src[(j*w+i)*3+0] * k); // red
							g += (src[(j*w+i)*3+1] * k); // green
							b += (src[(j*w+i)*3+2] * k); // blue
						}
					}
					r = ( r < 0 ? 0 : r);
					g = ( g < 0 ? 0 : g);
					b = ( b < 0 ? 0 : b);
					dst.setPixel(x, y, (((r) << 8) & 0xFF0000) | (g & 0xFF00) | ((b >> 8) & 0xFF));
				}
			}
		
		// border conditions
		int[][] bc =
				{
				{0, kernel.length, 0, w}, // north with edges
				{h - kernel.length, h, 0, w}, // south with edges
				{kernel.length, h - kernel.length, 0, kernel[0].length}, // west without edges
				{kernel.length, h - kernel.length, w - kernel[0].length ,w} // east without edges
				};
		for(int a = 0; a < bc.length; a++){
			// calculate border zone north with edges
			for (int y = bc[a][0]; y < bc[a][1]; y++) {
				for (int x = bc[a][2]; x < bc[a][3]; x++) {
					r = 0;g = 0;b = 0;
					for (int j = (y - kmh), ky = 0; j < (y + kmh + 1); j++, ky++) {
						for (int i = (x - kmw), kx = 0; i < (x + kmw + 1); i++, kx++) {
							int k = kernel[kx][ky]; // weight kernel
							int by = (0 > j ? 0 : ((h - 1) < j ? (h - 1) : j));
							int bx = (0 > i ? 0 : ((w - 1) < i ? (w - 1) : i));
							r += (src[(by*w+bx)*3+0] * k); // red
							g += (src[(by*w+bx)*3+1] * k); // green
							b += (src[(by*w+bx)*3+2] * k); // blue
						}
					}
					r = ( r < 0 ? 0 : r);
					g = ( g < 0 ? 0 : g);
					b = ( b < 0 ? 0 : b);
					dst.setPixel(x, y, (((r) << 8) & 0xFF0000) | (g & 0xFF00) | ((b >> 8) & 0xFF));
				}
			}
		}
		return dst;
	}

	/**
	 * apply a kernel to the source image and get an image
	 * @param src image
	 * @param w image width
	 * @param h image height
	 * @param kernel Kernel
	 * @return result
	 */
	public static Bitmap applyKernel(int[] src, int w, int h, float[][] kernel) {
		Bitmap dst = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
		int kmw = (kernel[0].length - 1) / 2; // kernel mean width
		int kmh = (kernel.length - 1) / 2; // kernel mean height

		float r, g, b;
		
		// calculate new picture without border
			for (int y = kernel.length; y < (h - kernel.length); y++) {
				// set pixel at pos. y
				for (int x = kernel[0].length; x < (w - kernel[0].length); x++) {
					// set pixel at pos. x
					// read neighbor and calculate new pixel
					r = 0.0f;g = 0.0f;b = 0.0f;
					for (int j = (y - kmh), ky = 0; j < (y + kmh + 1); j++, ky++) {
						for (int i = (x - kmw), kx = 0; i < (x + kmw + 1); i++, kx++) {
							float k = kernel[kx][ky]; // weight kernel
							r += (src[(j*w+i)*3+0] * k); // red
							g += (src[(j*w+i)*3+1] * k); // green
							b += (src[(j*w+i)*3+2] * k); // blue
						}
					}
					r = ( r < 0 ? 0 : r);
					g = ( g < 0 ? 0 : g);
					b = ( b < 0 ? 0 : b);
					dst.setPixel(x, y, ((((int)r) << 8) & 0xFF0000) | (((int)g) & 0xFF00) | ((((int)b) >> 8) & 0xFF));
				}
			}
		
		// border conditions
		int[][] bc =
				{
				{0, kernel.length, 0, w}, // north with edges
				{h - kernel.length, h, 0, w}, // south with edges
				{kernel.length, h - kernel.length, 0, kernel[0].length}, // west without edges
				{kernel.length, h - kernel.length, w - kernel[0].length ,w} // east without edges
				};
		for(int a = 0; a < bc.length; a++){
			// calculate border zone north with edges
			for (int y = bc[a][0]; y < bc[a][1]; y++) {
				for (int x = bc[a][2]; x < bc[a][3]; x++) {
					r = 0;g = 0;b = 0;
					for (int j = (y - kmh), ky = 0; j < (y + kmh + 1); j++, ky++) {
						for (int i = (x - kmw), kx = 0; i < (x + kmw + 1); i++, kx++) {
							float k = kernel[kx][ky]; // weight kernel
							int by = (0 > j ? 0 : ((h - 1) < j ? (h - 1) : j));
							int bx = (0 > i ? 0 : ((w - 1) < i ? (w - 1) : i));
							r += (src[(by*w+bx)*3+0] * k); // red
							g += (src[(by*w+bx)*3+1] * k); // green
							b += (src[(by*w+bx)*3+2] * k); // blue
						}
					}
					r = ( r < 0 ? 0 : r);
					g = ( g < 0 ? 0 : g);
					b = ( b < 0 ? 0 : b);
					dst.setPixel(x, y, ((((int)r) << 8) & 0xFF0000) | (((int)g) & 0xFF00) | ((((int)b) >> 8) & 0xFF));
				}
			}
		}
		return dst;
	}
}
